package com.study.sbsummary.config.notice;


import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.classic.spi.IThrowableProxy;
import ch.qos.logback.classic.spi.StackTraceElementProxy;
import ch.qos.logback.classic.spi.ThrowableProxy;
import ch.qos.logback.core.UnsynchronizedAppenderBase;
import com.study.sbsummary.config.cache.CodisClient;
import com.study.sbsummary.service.DingService;
import com.study.sbsummary.utils.SpringContextUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;

import java.net.InetAddress;

/**
 * @Author: yuliang
 * @Date: 2022/3/22
 */
@Slf4j
@Component
public class SendErrorMsgAppender extends UnsynchronizedAppenderBase<ILoggingEvent> {

    //换行符
    private static final String NEWLINE = "\n";

    //过滤标识
    private static final String FILTER_FLAG = "FILTER";

    //ip信息
    private static String IP;

    static {
        try {
            IP = InetAddress.getLocalHost().getHostAddress();
        } catch (Exception e) {
            log.error("获取日志Ip异常", e);
        }
    }

    @Override
    protected void append(ILoggingEvent event) {
        //如果日常级别是Error级别
        if (event.getLevel() == Level.ERROR) {
            //处理错误信息
            String msg = assemblyErrorMsg(event);
            if (StringUtils.isNotEmpty(msg) && !FILTER_FLAG.equals(msg)) {
                //获取dingService对象
                DingService dingService = SpringContextUtil.getBean("dingService");
                dingService.sendDing(msg, null);
            }
        }
    }

    /**
     * 封装错误信息
     *
     * @param event 日志事件对象
     * @return 发送的错误日志信息
     */
    private String assemblyErrorMsg(ILoggingEvent event) {
        //获取Throwable代理对象
        IThrowableProxy itp = event.getThrowableProxy();
        //错误信息主体
        StringBuilder sb = new StringBuilder();
        //获取服务Ip,告知哪台服务器抛异常
        sb.append("ip-").append(IP).append(NEWLINE);
        //拼接异常信息
        if (itp instanceof ThrowableProxy) {
            //转成throwable代理
            ThrowableProxy tp = (ThrowableProxy) itp;
            //获取Throwable对象
            Throwable ex = tp.getThrowable();
            //log文本信息
            String logMsg = event.getMessage();
            if (StringUtils.isNotEmpty(logMsg)){
                sb.append(logMsg).append(NEWLINE);
            }
            //获取异常信息
            String tMsg = ex.toString();
            if (StringUtils.isNotEmpty(tMsg)) {
                sb.append(tMsg).append(NEWLINE);
            }
            //获取堆栈信息
            StackTraceElementProxy[] stackArr = itp.getStackTraceElementProxyArray();
            //获取堆栈的第一条异常信息
            String stackMsg = stackArr[0].getSTEAsString();
            //拼接过滤的key
            String filterKey = spliceKey(logMsg, tMsg, stackMsg);
            //过滤重复异常信息
            if (!filterRepeatError(filterKey)) {
                return FILTER_FLAG;
            }
            int i = 0;
            for (StackTraceElementProxy proxy : stackArr) {
                //只打印40行的堆栈
                if (i < 40) {
                    sb.append(proxy.getSTEAsString()).append(NEWLINE);
                } else {
                    break;
                }
                i++;
            }
        } else {
            //获取message信息
            String message = event.getMessage();
            if (StringUtils.isBlank(message)){
                return FILTER_FLAG;
            }
            //过滤重复的报警请求
            if (!filterRepeatError(event.toString())){
                return FILTER_FLAG;
            }
            sb.append(event);
        }
        return sb.toString();
    }

    /**
     * 判断当前错误信息是否需要打印
     *
     * @param msg 错误信息
     * @return 是否过滤
     */
    private Boolean filterRepeatError(String msg) {
        CodisClient codisClient = SpringContextUtil.getBean("codisClient");
        return codisClient.getLock(msg, String.valueOf(System.currentTimeMillis()), 5 * 60);
    }

    /**
     * 拼接异常过滤唯一key
     *
     * @param logMsg   logMessage信息
     * @param eMsg     异常e类名+message信息
     * @param stackMsg 最底部的栈异常信息
     * @return String =>
     */
    private String spliceKey(String logMsg, String eMsg, String stackMsg) {
        StringBuilder sbk = new StringBuilder();
        //异常对象信息
        if (StringUtils.isNotEmpty(logMsg)) {
            sbk.append(logMsg);
        }
        //异常logMessage信息
        if (StringUtils.isNotEmpty(eMsg)) {
            sbk.append("-").append(eMsg);
        }
        //栈异常信息
        if (StringUtils.isNotEmpty(stackMsg)) {
            String className = stackMsg.split("\\(")[0];
            String methodName = className.substring(className.lastIndexOf(".") + 1);
            if (StringUtils.isNotEmpty(methodName)) {
                sbk.append("-").append(methodName);
            }
        }
        return StringUtils.deleteWhitespace(sbk.toString());
    }
}
